package com.wmx.hb.service;

import com.wmx.hb.mapper.EmpMapper;
import com.wmx.hb.pojo.Emp;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 * Emp 员工业务层
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/9/12 15:32
 */
@Service
@SuppressWarnings("all")
public class EmpService {
    //XxxMapper 接口上可以不用写 @Repository、@Service 注解，直接就能获取到.
    @Resource
    private EmpMapper empMapper;

    //SqlSessionFactory 是线程安全的.
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批量插入方式 1 —— 底层是单个元素循环插入
     *
     * @param empList
     */
    @Transactional
    public void batchInserts(List<Emp> empList) {
        long start = System.currentTimeMillis();
        if (empList != null && empList.isEmpty()) {
            return;
        }
        for (Emp emp : empList) {
            empMapper.insertEmp(emp);
        }
        long end = System.currentTimeMillis();
        //1000 条，单个元素循环插入耗时(毫秒)：3414
        //1000 条，单个元素循环插入耗时(毫秒)：3292
        //1000 条，单个元素循环插入耗时(毫秒)：3248
        System.out.println(empList.size() + "条，单个元素循环插入耗时(毫秒)：" + (end - start));
    }

    /**
     * 批量插入方式 2 —— 底层通过 sql 实现，效率会高于逐个插入
     * 1、insert into tableName(k1,k2,k3) values (v1, v2, v3),(v1, v2, v3);
     * 2、注意事项，因为是通过一个 SQL 完成，虽然效率高，但是 sql 字符串长度也不能无限制的太长，防止数据库底层报错。
     * 3、实际开发中，根据实际情况，设置批量插入的条数，比如 500，800，1000 条一次.
     *
     * @param empList
     */
    @Transactional
    public void batchAdds(List<Emp> empList) {
        long start = System.currentTimeMillis();
        if (empList != null && empList.isEmpty()) {
            return;
        }
        empMapper.insertEmps(empList);
        long end = System.currentTimeMillis();
        //1000 条，insert into xxx values (v1, v2, v3),(v1, v2, v3) 插入耗时(毫秒)：177
        //1000 条，insert into xxx values (v1, v2, v3),(v1, v2, v3) 插入耗时(毫秒)：156
        //1000 条，insert into xxx values (v1, v2, v3),(v1, v2, v3) 插入耗时(毫秒)：155
        System.out.println(empList.size() + " 条，insert into xxx values (v1, v2, v3),(v1, v2, v3) 插入耗时(毫秒)：" + (end - start));
    }

    /**
     * 批量插入方式 3 —— 底层通过 SqlSession 的批处理，这也是官方提供的示例.
     *
     * @param empList
     */
    @Transactional
    public void batchSaves(List<Emp> empList) {
        long start = System.currentTimeMillis();

        //SqlSession openSession(ExecutorType execType, boolean autoCommit)
        //ExecutorType.BATCH ：表示批处理，false：关闭自动提交(不写时默认也是false).
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
        int count = 0;
        for (Emp emp : empList) {
            empMapper.insertEmp(emp);
            //每500、10000 个时提交一次，防止内存溢出.
            if (++count >= 500) {
                sqlSession.commit();
                count = 0;
            }
        }
        //最后再提交一次
        sqlSession.commit();
        long end = System.currentTimeMillis();
        //1000 条，SqlSession 批处理耗时(毫秒)：714
        //1000 条，SqlSession 批处理耗时(毫秒)：834
        //1000 条，SqlSession 批处理耗时(毫秒)：711
        System.out.println(empList.size() + " 条，SqlSession 批处理耗时(毫秒)：" + (end - start));
    }

    /**
     * 批量插入方式 4 —— 底层使用 Oralce Insert All 语法批量插入
     *
     * @param empList
     */
    @Transactional
    public void batchInserts2(List<Emp> empList) {
        long start = System.currentTimeMillis();
        empMapper.batchInserts(empList);
        long end = System.currentTimeMillis();
        //1000 条，单个元素循环插入耗时(毫秒)：
        //1000 条，单个元素循环插入耗时(毫秒)：
        //1000 条，单个元素循环插入耗时(毫秒)：
        System.out.println(empList.size() + "条，Insert All 语法批量插入耗时(毫秒)：" + (end - start));
    }


}
